package com.example.demo.config.jwt;

import cn.hutool.json.JSONUtil;
import com.example.demo.constant.CommonConstants;
import com.example.demo.util.R;
import com.example.demo.util.jwt.JwtUtil;
import io.jsonwebtoken.*;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Component
public class JwtFilter extends GenericFilter {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
        httpServletResponse.setCharacterEncoding("UTF-8");
        try {
            if (request instanceof HttpServletRequest) {
                HttpServletRequest httpRequest = (HttpServletRequest) request;

                //登录请求直接放过
                if (httpRequest.getRequestURI().equals("/login1")) {
                    chain.doFilter(request, response);
                    return;
                }else {
                    String token = httpRequest.getHeader(CommonConstants.authorization);
                    if (token != null && token.startsWith(CommonConstants.bearer + " ")) {
                        token = token.replaceAll(CommonConstants.bearer + " ", "");
                        boolean b = JwtUtil.validateToken(token);
                        if (b){
                            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(JwtUtil.SECRET_KEY).parseClaimsJws(token);
                            Claims claims = claimsJws.getBody();
                            //用户名
                            String username = claims.getSubject();
                            //检查令牌
                            String key = username+":"+token;
                            String redisToken = redisTemplate.opsForValue().get(key);
                            if (redisToken == null) {
                                throw new RuntimeException("令牌已失效");
                            }else{
                                //权限
                                List<String> authorities = claims.get("authorities", List.class);
                                //todo 故意打印看卡里面有没有数据,为什么一个hello请求 会进来这个地方两次，第一次authentication没有值，第二次有值
                                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                                log.info("{}",JSONUtil.toJsonStr(authentication));
                                List<SimpleGrantedAuthority> simpleGrantedAuthorityList = authorities.stream().map(o -> new SimpleGrantedAuthority(o)).collect(Collectors.toList());
                                //很关键，存入SecurityContextHolder中
                                UsernamePasswordAuthenticationToken authenticated = UsernamePasswordAuthenticationToken.authenticated(username, null, simpleGrantedAuthorityList);
                                SecurityContextHolder.getContext().setAuthentication(authenticated);
                                //令牌有效，则刷新令牌
                                redisTemplate.expire(key, Duration.ofSeconds(30 * 60 * 60));

                                chain.doFilter(request, response);
                            }

                        }else {
                            throw new RuntimeException("令牌验证失败");
                        }
                    }else {
                        throw new RuntimeException("令牌为空，请求失败");
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            httpServletResponse.getWriter().write(JSONUtil.toJsonStr(e.getMessage()));
            httpServletResponse.getWriter().flush();
            httpServletResponse.getWriter().close();
        } finally {
            //清除上下文，否则有可能导致内存泄露
            SecurityContextHolder.clearContext();
        }
    }

    @Override
    public void destroy() {
        super.destroy();
    }
}
